In [22]:
import networkx as nx
import pandas as pd
import matplotlib.pyplot as plt

import numpy as np
import seaborn as sns
import random
import operator

import matplotlib.patches as mpatches

Infection Modeling

Pandemic modeling can be compartmentalized into different state, the simplest of which is the SI model. The model consists of two compartments, specifically:

  • Susceptible. The number of individuals that can be infected.
  • Infected. The number of individuals who are infected.

Other variants of compartmentalized models such as SIS, SIR, SEIR exists, but to simplifiy the modeling, we will be using the SI model.

Creation of Social Network Graph

The following network attributes were selected arbitrarily:

  • Number of Nodes: 300
  • Percentage of infected: 5%

While the beta value of 0.10 and the consideration for watts strogatz model was selected based on Professor Erika Legara's notebook.(https://github.com/eflegara/Network-Science-Lectures/blob/master/Exploring%20Social%20Distancing.ipynb)

Watts strogatz was created as the clusters of network reflects social clustering and encourages the spread of attribute in the network.

In [23]:
#pos = nx.fruchterman_reingold_layout(g);
N = 300
percentage = 0.05
beta = 0.10
g = nx.watts_strogatz_graph(N, 5, 0.89)
nx.set_node_attributes(g, 0, 'infected')
nx.set_node_attributes(g, 0, 'vaccinated')
nx.set_node_attributes(g, 0, 'quarantined')


#set infected nodes
for i in range (int(N*percentage)):
    g.nodes(data=True)[random.choice(list(g.nodes))]['infected'] = 1
    
infected = {k:v for k,v in nx.get_node_attributes(g, "infected").items() 
            if v==1}
  
print(f'Initial infected people: {len(infected.values())}')
Initial infected people: 15
In [24]:
pos = nx.spring_layout(g, seed=18291)

node_colors = []
for node in g.nodes(data=True):
    if node[1]['infected'] == 1:
        node_colors.append('#fb1700')
    else:
        node_colors.append('#95c4cc')

# Plot network
plt.figure(figsize=(16, 12))
plt.axis("off")

dg_centrality = np.array(list(nx.degree_centrality(g).values()))

nx.draw_networkx_nodes(g, pos, alpha=0.75, node_color=node_colors,
                      node_size=dg_centrality*10_000)
nx.draw_networkx_edges(g, pos, alpha=0.30);

infected_patch = mpatches.Patch(color='#fb1700', label='infected')
healthy_patch = mpatches.Patch(color='#95c4cc', label='healthy')

plt.legend(handles=[infected_patch, healthy_patch])

plt.title('Visualization of affected nodes');

Simulating Infection

The simulation was inspired by Professor Erika Legara's agent based modeling in her notebook on social distancing (https://github.com/eflegara/Network-Science-Lectures/blob/master/Exploring%20Social%20Distancing.ipynb). Instead of implementing an object oriented agent based model, we leveraged the network attributes of nodes to define the state of the agent/nodes.

In [25]:
infected_list = []

g_copy = g.copy()
for time in range(10):
    graph = g_copy.copy()
    for node in g_copy.nodes(data=True):
        if node[1]['infected'] == 1:
            for edge in g_copy.edges(node[0]):    
                if (random.randint(0,100)/100) < beta:
                    graph.nodes(data=True)[edge[1]]['infected'] = 1                    
                    #node[1]['infected'] = 1
                    
    infected = {k:v for k,v 
                in nx.get_node_attributes(graph, "infected").items() 
                if v==1}
    infected_list.append(len(infected))
    
    g_copy = graph
    
    pos = nx.spring_layout(g_copy, seed=18291)

    node_colors = []
    for node in g_copy.nodes(data=True):
        if node[1]['infected'] == 1:
            node_colors.append('#fb1700')
        else:
            node_colors.append('#95c4cc')

    # Plot network
    plt.figure(figsize=(16, 12))
    plt.axis("off")
    dg_centrality = np.array(list(nx.degree_centrality(g_copy).values()))

    nx.draw_networkx_nodes(g_copy, pos, alpha=0.75, node_color=node_colors,
                      node_size=dg_centrality*10_000)
    nx.draw_networkx_edges(g_copy, pos, alpha=0.30);
In [26]:
fig, ax = plt.subplots(figsize=(10,5))
sns.lineplot(x=range(len(infected_list)), y=infected_list, );
ax.set_title('Rate of Infection')
ax.set_xlabel('Time')
ax.set_ylabel('Number of Infected');
In [30]:
print(f'Number of infected nodes {infected_list[-1]}')
print(f'Percentage of infected population: {infected_list[-1]/N}')
Number of infected nodes 146
Percentage of infected population: 0.4866666666666667
In [28]:
per_day_change = np.array(infected_list[1:]) - np.array(infected_list[:-1])

From the original 0.5% of population being infected, the number of infected nodes rose up to 48.66% after 10 iterations or timesteps.

Quarantine

The quarantine simulation was modeled based on the notion of infected people/nodes should be quarantined. Once quarantined, the transmission of disease of the node will cease. There are few rules considered in the quarantine:

  1. Infected nodes are not quarantined as soon as they are infected.
  2. Infected nodes can still infect other nodes one timestep after they are infected.
  3. After two timestep, the node will be tagged as quarantined and will not be able to transmit in the succeeding timestep.

For illustration, suppose node 5 was infected on Day 1. Node 5 will not be tagged as quarantined on Day 2 and will still be able to infect other nodes based on the beta probability. Starting day 3, node 5 will be tagged as quarantined and will not be able to infect other nodes.

In [31]:
infected_list = []

g_copy = g.copy()
for time in range(5):
    graph = g_copy.copy()
    for node in g_copy.nodes(data=True):
        if node[1]['infected'] == 1 and node[1]['quarantined'] == 0:
            for edge in g_copy.edges(node[0]):    
                if (random.randint(0,100)/100) < beta:
                    graph.nodes(data=True)[edge[1]]['infected'] = 1                    
                    #node[1]['infected'] = 1
            
            graph.nodes(data=True)[node[0]]['quarantined'] = 1            
    infected = {k:v for k,v 
                in nx.get_node_attributes(graph, "infected").items() 
                if v==1}
    infected_list.append(len(infected))
    
    g_copy = graph
    
    pos = nx.spring_layout(g_copy, seed=18291)

    node_colors = []
    for node in g_copy.nodes(data=True):
        if node[1]['quarantined'] == 1:
            node_colors.append('#fad000')
        elif node[1]['infected'] == 1:
            node_colors.append('#fb1700')        
        else:
            node_colors.append('#95c4cc')

    # Plot network
    plt.figure(figsize=(16, 12))
    plt.axis("off")
    dg_centrality = np.array(list(nx.degree_centrality(g_copy).values()))

    nx.draw_networkx_nodes(g_copy, pos, alpha=0.75, node_color=node_colors,
                      node_size=dg_centrality*10_000)
    nx.draw_networkx_edges(g_copy, pos, alpha=0.30);
In [9]:
fig, ax = plt.subplots(figsize=(10,5))
sns.lineplot(x=range(len(infected_list)), y=infected_list, );
ax.set_title('Rate of Infection')
ax.set_xlabel('Time')
ax.set_ylabel('Number of Infected');
In [32]:
print(f'Number of infected nodes {infected_list[-1]}')
print(f'Percentage of infected population: {infected_list[-1]/N}')
Number of infected nodes 21
Percentage of infected population: 0.07

We can see that when infected nodes are quarantined as soon as possible, the possibility of transmission was greatly reduced. All infected nodes are quarantined by the third timestep which capped the number of infected ndoes to 21 or 7% of the population.

The network showed that prevention of transmission as early as possible can halt the spread of transmission within the network.

Vaccination

Research shows that fully vaccinated people are not immune to COVID-19 virus and are still able to transmit the virus to other person (CDC, 2021). To model the vaccination program, the network will still be able to infect vaccinated people, however; vaccinated people has less possibility to transmit the virus to other nodes.

Arbitrarily, we divided the beta by 20 for vaccinated nodes to signify the reduce possibility of transmission for vaccinated people.

We will be comparing two vaccination strategy. One is a randomized vaccination where randomly selected nodes will be tagged as vaccinated while another strategy targets the nodes with high degree centrality.

Random Vaccination

In [10]:
percentage = 0.05
infected_list = []
g_copy = g.copy()
for i in range (int(N*percentage)):
    g_copy.nodes(data=True)[random.choice(list(g_copy.nodes))]['vaccinated'] = 1


for time in range(10):
    graph = g_copy.copy()
    for node in g_copy.nodes(data=True):
        if node[1]['infected'] == 1:
            for edge in g_copy.edges(node[0]):    
                new_beta = beta
                
                if node[1]['vaccinated']:
                    new_beta = beta/20          
                    
                if (random.randint(0,100)/100) < new_beta:
                    graph.nodes(data=True)[edge[1]]['infected'] = 1                    
                    #node[1]['infected'] = 1
            
            graph.nodes(data=True)[node[0]]['quarantined'] = 1            
    infected = {k:v for k,v 
                in nx.get_node_attributes(graph, "infected").items() 
                if v==1}
    infected_list.append(len(infected))
    
    g_copy = graph
    
    pos = nx.spring_layout(g_copy, seed=18291)

    node_colors = []
    for node in g_copy.nodes(data=True):
        if node[1]['infected'] == 1:
            node_colors.append('#fb1700')        
        elif node[1]['vaccinated'] == 1:
            node_colors.append('#00005c')        
        else:
            node_colors.append('#95c4cc')

    # Plot network
    plt.figure(figsize=(16, 12))
    plt.axis("off")
    dg_centrality = np.array(list(nx.degree_centrality(g_copy).values()))

    nx.draw_networkx_nodes(g_copy, pos, alpha=0.75, node_color=node_colors,
                      node_size=dg_centrality*10_000)
    nx.draw_networkx_edges(g_copy, pos, alpha=0.30);
    
random_list = infected_list

Targeted Vaccination

In [11]:
percentage = 0.05
infected_list = []
degree_centrality = {k: v for k, v in 
                     sorted(dict(nx.degree_centrality(g_copy)).items(), 
                            key=lambda item: item[1], 
                            reverse=True)[:int(N*percentage)]}
g_copy = g.copy()
for k,v in degree_centrality.items():
    g_copy.nodes(data=True)[k]['vaccinated'] = 1


for time in range(10):
    graph = g_copy.copy()
    for node in g_copy.nodes(data=True):
        if node[1]['infected'] == 1:
            for edge in g_copy.edges(node[0]):    
                new_beta = beta
                
                if node[1]['vaccinated']:
                    new_beta = beta/20          
                    
                if (random.randint(0,100)/100) < new_beta:
                    graph.nodes(data=True)[edge[1]]['infected'] = 1                    
                    #node[1]['infected'] = 1
            
            graph.nodes(data=True)[node[0]]['quarantined'] = 1            
    infected = {k:v for k,v 
                in nx.get_node_attributes(graph, "infected").items() 
                if v==1}
    infected_list.append(len(infected))
    
    g_copy = graph
    
    pos = nx.spring_layout(g_copy, seed=18291)

    node_colors = []
    for node in g_copy.nodes(data=True):
        if node[1]['infected'] == 1:
            node_colors.append('#fb1700')        
        elif node[1]['vaccinated'] == 1:
            node_colors.append('#00005c')        
        else:
            node_colors.append('#95c4cc')

    # Plot network
    plt.figure(figsize=(16, 12))
    plt.axis("off")
    dg_centrality = np.array(list(nx.degree_centrality(g_copy).values()))

    nx.draw_networkx_nodes(g_copy, pos, alpha=0.75, node_color=node_colors,
                      node_size=dg_centrality*10_000)
    nx.draw_networkx_edges(g_copy, pos, alpha=0.30);
    
targeted_list = infected_list

Comparison

In [17]:
fig, ax = plt.subplots(1, 2, figsize=(15,5))
sns.lineplot(x=range(len(random_list)), y=random_list, ax=ax[0]);
ax[0].set_title('Rate of Infection of Random Vaccination')
ax[0].set_xlabel('Time') 
ax[0].set_ylabel('Number of Infected');

sns.lineplot(x=range(len(targeted_list)), y=targeted_list, ax=ax[1]);
ax[1].set_title('Rate of Infection of Targeted Vaccinatin')
ax[1].set_xlabel('Time')
ax[1].set_ylabel('Number of Infected');
In [35]:
print(f'Number of infected nodes - random list {random_list[-1]}')
print(f'Percentage of infected population - random list: {random_list[-1]/N}')

print('')
print(f'Number of infected nodes - targeted list {targeted_list[-1]}')
print(f'Percentage of infected population - targeted list  {targeted_list[-1]/N}')
Number of infected nodes - random list 135
Percentage of infected population - random list: 0.45

Number of infected nodes - targeted list 105
Percentage of infected population - targeted list  0.35

Comparing the two vaccination strategy, we can see that targeted vaccination reduces the transmission between nodes. It shows that the vaccination of people with high number of connection could strengthen the immunity and robustness of the network.

References

Barabasi, A. (n.d.). Network Science. Http://Networksciencebook.Com/Chapter/4#generating-Networks. Retrieved March 15, 2021, from http://networksciencebook.com/chapter/4#generating-networks

https://www.cdc.gov/coronavirus/2019-ncov/science/science-briefs/fully-vaccinated-people.html

Legara, E. (2021). Exploring Social Distancing. [Git Repository]. Retrieved from https://github.com/eflegara/Network-Science-Lectures/blob/master/Exploring%20Social%20Distancing.ipynb

In [ ]: